package naga.x.db.impl;

import java.util.Objects;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import gnu.trove.map.TIntObjectMap;
import gnu.trove.map.hash.TIntObjectHashMap;
import gnu.trove.procedure.TIntObjectProcedure;
import naga.x.db.DBSession;
import naga.x.db.DBSessionFactory;
import naga.x.db.DBSessionListener;

public class SqlSessionFactory implements DBSessionFactory, DBSessionListener {

	protected org.apache.ibatis.session.SqlSessionFactory factory;
	protected TIntObjectMap<DBSession> sessions;
	protected Lock lock = new ReentrantLock();
	protected byte state = INITED;
	
	public static final byte INITED = 1, CLOSING = 2, CLOSED = 3;
	
	public SqlSessionFactory(org.apache.ibatis.session.SqlSessionFactory factory) {
		this.factory = Objects.requireNonNull(factory);
		this.sessions = new TIntObjectHashMap<DBSession>();
	}
	
	@Override
	public DBSession getSession() {
		if (state != INITED) {
			return null;
		}
		return new SqlSession(factory.openSession(), this);
	}
	
	@Override
	public void sessionOpened(DBSession session) {
		if (state == INITED) {
			lock.lock();
			try {
				sessions.put(session.getId(), session);
			} finally {
				lock.unlock();
			}
		}
	}

	@Override
	public void sessionClosed(DBSession session) {
		if (state == INITED) {
			lock.lock();
			try {
				sessions.remove(session.getId());
			} finally {
				lock.unlock();
			}
		}
	}
	
	@Override
	public void close() {
		state = CLOSING;
		lock.lock();
		try {
			sessions.forEachEntry(new TIntObjectProcedure<DBSession>() {
				@Override
				public boolean execute(int id, DBSession session) {
					try {
						session.commit();
					} catch (Exception e) {
						session.rollback();
					}
					session.close();
					return true;
				}
			});
			sessions.clear();
		} finally {
			lock.unlock();
		}
		state = CLOSED;
	}

}
